#include "enviroment/input.h"

UInput::UInput() : m_key_events(std::unordered_map<EKeyCode, UInput::__key_events_pack>()), m_mouse_position({0, 0}), m_mouse_button(std::unordered_map<EMouseButton, uBoolean>())
{
#ifdef UINPUT_KEYBOARD_STATE_AGENT
    m_key_states = const_cast<uint8 *>(SDL_GetKeyboardState(nullptr));
#else
    m_key_states = nullptr;
    m_keys_map = std::unordered_map<EKeyCode, uBoolean>();
#endif
    m_mouse_button.insert(std::make_pair(EMouseButton::Left, false));
    m_mouse_button.insert(std::make_pair(EMouseButton::Middle, false));
    m_mouse_button.insert(std::make_pair(EMouseButton::Right, false));
}

UInput::~UInput()
{
    m_key_events.clear();
}

void UInput::BindEvent(EKeyCode key, UInput::InputType type, InputEventFunc ev)
{
    auto find = m_key_events.find(key);
    if (find != m_key_events.end())
    {
        (*find).second = std::make_pair(type, ev);
    }
    m_key_events.insert(std::make_pair(key, std::make_pair(type, ev)));
}

void UInput::UnbindEvent(EKeyCode key, UInput::InputType type)
{
    auto find = m_key_events.find(key);
    if (find != m_key_events.end())
    {
        if ((*find).second.first == type)
        {
            m_key_events.erase(find);
        }
    }
}

uBoolean UInput::KeyHasBindEvent(EKeyCode key)
{
    auto find = m_key_events.find(key);
    if (find != m_key_events.end())
    {
        return true;
    }
    return false;
}

uBoolean UInput::GetKey(EKeyCode key)
{
#ifdef UINPUT_KEYBOARD_STATE_AGENT
    return !!(m_key_states[(int32)key]);
#else
    auto kf = m_keys_map.find(key);
    if(kf != m_keys_map.end()){
        return (*m_keys_map.find(key)).second;
    }
    return false;
#endif
}

uBoolean UInput::GetMouse(EMouseButton mouse)
{
    auto find = m_mouse_button.find(mouse);
    if (find != m_mouse_button.end())
    {
        return (*find).second;
    }
    return false;
}

uPoint<int32> UInput::GetMousePosition()
{
    return this->m_mouse_position;
}

uPoint<int32> UInput::GetMousePositionFromAgent()
{
    int32 x, y;
    SDL_GetMouseState(&x, &y);
    return {x, y};
}

void UInput::__update_mouse()
{
    SDL_GetMouseState(&this->m_mouse_position.X, &this->m_mouse_position.Y);
}

// global object
UInput *GInputer = new UInput();